/* ioapi.h -- IO base function header for compress/uncompress .zip
   part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )

         Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )

         Modifications for Zip64 support
         Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )

         For more info read MiniZip_info.txt

*/

#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lvgl.h"
#else
#include "../../../lvgl/lvgl.h"
#endif

#if defined(_WIN32) && (!(defined(_CRT_SECURE_NO_WARNINGS)))
#define _CRT_SECURE_NO_WARNINGS
#endif

#if defined(__APPLE__) || defined(IOAPI_NO_64)
// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
#define FOPEN_FUNC(filename, mode) fopen(filename, mode)
#define FTELLO_FUNC(stream) ftello(stream)
#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
#else
#define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
#define FTELLO_FUNC(stream) ftello64(stream)
#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
#endif


#include "ioapi.h"

voidpf call_zopen64(const zlib_filefunc64_32_def * pfilefunc, const void * filename, int mode)
{
    if(pfilefunc->zfile_func64.zopen64_file != NULL)
        return (*(pfilefunc->zfile_func64.zopen64_file))(pfilefunc->zfile_func64.opaque, filename, mode);
    else {
        return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque, (const char *)filename, mode);
    }
}

long call_zseek64(const zlib_filefunc64_32_def * pfilefunc, voidpf filestream, ZPOS64_T offset, int origin)
{
    if(pfilefunc->zfile_func64.zseek64_file != NULL)
        return (*(pfilefunc->zfile_func64.zseek64_file))(pfilefunc->zfile_func64.opaque, filestream, offset, origin);
    else {
        uLong offsetTruncated = (uLong)offset;
        if(offsetTruncated != offset)
            return -1;
        else
            return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque, filestream, offsetTruncated, origin);
    }
}

ZPOS64_T call_ztell64(const zlib_filefunc64_32_def * pfilefunc, voidpf filestream)
{
    if(pfilefunc->zfile_func64.zseek64_file != NULL)
        return (*(pfilefunc->zfile_func64.ztell64_file))(pfilefunc->zfile_func64.opaque, filestream);
    else {
        uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque, filestream);
        if((tell_uLong) == MAXU32)
            return (ZPOS64_T) - 1;
        else
            return tell_uLong;
    }
}

void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def * p_filefunc64_32, const zlib_filefunc_def * p_filefunc32)
{
    p_filefunc64_32->zfile_func64.zopen64_file = NULL;
    p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file;
    p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file;
    p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file;
    p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file;
    p_filefunc64_32->zfile_func64.ztell64_file = NULL;
    p_filefunc64_32->zfile_func64.zseek64_file = NULL;
    p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file;
    p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file;
    p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque;
    p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file;
    p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file;
}



static voidpf  ZCALLBACK fopen_file_func OF((voidpf opaque, const char * filename, int mode));
static uLong   ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void * buf, uLong size));
static uLong   ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void * buf, uLong size));
static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream));
static long    ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
static int     ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream));
static int     ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream));
static uLong   ZCALLBACK fsize_file_func OF((voidpf opaque, voidpf stream));


static voidpf ZCALLBACK fopen_file_func(voidpf opaque, const char * filename, int mode)
{
    lv_fs_file_t file;
    lv_fs_mode_t fs_mode = LV_FS_MODE_RD;
    voidpf * fp = NULL;

    if((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) {
        fs_mode = LV_FS_MODE_RD;
    } else if(mode & ZLIB_FILEFUNC_MODE_EXISTING) {
        fs_mode = LV_FS_MODE_RD | LV_FS_MODE_APPEND;
    } else if(mode & ZLIB_FILEFUNC_MODE_CREATE) {
        fs_mode = LV_FS_MODE_WR;
    }

    lv_fs_res_t res = lv_fs_open(&file, filename, fs_mode);
    if(LV_FS_RES_OK == res) {
        fp = file.file_d;
    }

    return fp;
}

static voidpf ZCALLBACK fopen64_file_func(voidpf opaque, const void * filename, int mode)
{
    return fopen_file_func(opaque, filename, mode);
}

static uLong ZCALLBACK fread_file_func(voidpf opaque, voidpf stream, void * buf, uLong size)
{
    lv_fs_file_t file;
    uint32_t br = 0;

    file.file_d = stream;
    file.drv = opaque;
    lv_fs_read(&file, buf, size, &br);
    return br;
}

static uLong ZCALLBACK fwrite_file_func(voidpf opaque, voidpf stream, const void * buf, uLong size)
{
    lv_fs_file_t file;
    uint32_t bw = 0;

    file.file_d = stream;
    file.drv = opaque;
    lv_fs_write(&file, buf, size, &bw);
    return bw;
}

static long ZCALLBACK ftell_file_func(voidpf opaque, voidpf stream)
{
    lv_fs_file_t file;
    uint32_t pos = 0;

    file.file_d = stream;
    file.drv = opaque;
    lv_fs_res_t res = lv_fs_tell(&file, &pos);
    if(LV_FS_RES_OK == res) {
        return pos;
    }
    return -1;
}

static ZPOS64_T ZCALLBACK ftell64_file_func(voidpf opaque, voidpf stream)
{
    return ftell_file_func(opaque, stream);
}

static long ZCALLBACK fseek_file_func(voidpf  opaque, voidpf stream, uLong offset, int origin)
{
    lv_fs_file_t file;
    uint32_t pos = 0;
    long ret = -1;

    file.file_d = stream;
    file.drv = opaque;

    switch(origin) {
        case ZLIB_FILEFUNC_SEEK_CUR :
            lv_fs_tell(&file, &pos);
            break;
        case ZLIB_FILEFUNC_SEEK_END :
            lv_fs_size(&file, &pos);
            break;
        case ZLIB_FILEFUNC_SEEK_SET :
            pos = 0;
            break;
        default :
            return -1;
    }
    pos += offset;

    lv_fs_res_t res = lv_fs_seek(&file, pos);
    if(LV_FS_RES_OK == res) {
        ret = 0;
    }
    return ret;
}

static long ZCALLBACK fseek64_file_func(voidpf  opaque, voidpf stream, ZPOS64_T offset, int origin)
{
    return fseek_file_func(opaque, stream, offset, origin);
}

static int ZCALLBACK fclose_file_func(voidpf opaque, voidpf stream)
{
    lv_fs_file_t file;
    int ret = -1;

    file.file_d = stream;
    file.drv = opaque;
    lv_fs_res_t res = lv_fs_close(&file);
    if(LV_FS_RES_OK == res) {
        ret = 0;
    }
    return ret;
}

static int ZCALLBACK ferror_file_func(voidpf opaque, voidpf stream)
{
    return 0;
}

static uLong ZCALLBACK fsize_file_func(voidpf opaque, voidpf stream)
{
    lv_fs_file_t file;
    uint32_t size = 0;

    file.file_d = stream;
    file.drv = opaque;
    lv_fs_size(&file, &size);
    return size;
}

void fill_fopen_filefunc(zlib_filefunc_def * pzlib_filefunc_def)
{
    pzlib_filefunc_def->zopen_file = fopen_file_func;
    pzlib_filefunc_def->zread_file = fread_file_func;
    pzlib_filefunc_def->zwrite_file = fwrite_file_func;
    pzlib_filefunc_def->ztell_file = ftell_file_func;
    pzlib_filefunc_def->zseek_file = fseek_file_func;
    pzlib_filefunc_def->zclose_file = fclose_file_func;
    pzlib_filefunc_def->zerror_file = ferror_file_func;
    pzlib_filefunc_def->zsize_file = fsize_file_func;
    pzlib_filefunc_def->opaque = NULL;
}

void fill_fopen64_filefunc(zlib_filefunc64_def * pzlib_filefunc_def)
{
    pzlib_filefunc_def->zopen64_file = fopen64_file_func;
    pzlib_filefunc_def->zread_file = fread_file_func;
    pzlib_filefunc_def->zwrite_file = fwrite_file_func;
    pzlib_filefunc_def->ztell64_file = ftell64_file_func;
    pzlib_filefunc_def->zseek64_file = fseek64_file_func;
    pzlib_filefunc_def->zclose_file = fclose_file_func;
    pzlib_filefunc_def->zerror_file = ferror_file_func;
    pzlib_filefunc_def->zsize_file = fsize_file_func;

    char letter = *(char *)pzlib_filefunc_def->opaque;
    pzlib_filefunc_def->opaque = lv_fs_get_drv(letter);
}
